
package picsim;

/**
 *
 * @author Daniel
 * @brief Die Klasse enthält für die Simulation des Microcontrollers Abbildungen aller Befehle des PIC.
 * Die Befehle werden in gleichnamigen Methoden dieser Klasse ausgeführt.
 * 
 */
public class PIC {

    private MemMan mem;
    private Calculator calc;

    int result = 0;

    /**
     * 
     * @param pMemMan (Object)
     * @brief Konstruktor der Klasse. Macht dem PIC das Objekt des Speichers (Klasse MemMan und Calculator) bekannt.
     */
    public PIC(MemMan pMemMan) {
        mem = pMemMan;
        calc = new Calculator();
    }



    /**
     * @brief Implementiert die Interrupt Funktionalität
     */
    public void interrupts() {

        //RB0
        if(mem.getPORTBRegValue(0) != -1) { // changed value
            if (mem.getPORTBprevRegValue(0) != mem.getOptionRegValue(6))  {

                if(mem.getOptionRegValue(6) == 1 && mem.getPORTBprevRegValue(0) == 0 && mem.getOptionRegValue(6) == 1) { // rising edge
                    mem.setINTCONRegValue(1, 1);
                }

                if(mem.getOptionRegValue(6) == 0 && mem.getPORTBprevRegValue(0) == 1 && mem.getOptionRegValue(6) == 0) { // falling edge
                    mem.setINTCONRegValue(1, 1);
                }

            }
        }

        //Port B Pin 4-7
        for(int i=4; i<=7; i++) {
            if(mem.getPORTBprevRegValue(i) != -1 && (mem.getPORTBprevRegValue(i) != mem.getPORTBRegValue(i))) {
                mem.setINTCONRegValue(0, 1); // an input change occured
            }
        }
        
        // interrupt to execute?
        int GIE = mem.getINTCONRegValue(7);

        int T0IE = mem.getINTCONRegValue(5);
        int INTE = mem.getINTCONRegValue(4);
        int RBIE = mem.getINTCONRegValue(3);
        int T0IF = mem.getINTCONRegValue(2);
        int INTF = mem.getINTCONRegValue(1);
        int RBIF = mem.getINTCONRegValue(0);

        if(GIE == 1) {

            //timer0 interrupt?
            if(T0IF == 1 && T0IE == 1) {
                mem.setINTCONRegValue(7, 0); // clear GIE
                if(Integer.parseInt(mem.getLastOpcode(),16) > mem.getPCL()) {
                    mem.push(mem.getPCL()+1);
                }
                else {
                    mem.push(Integer.parseInt(mem.getLastOpcode(),16));
                }
                mem.setPCL(4);
            }

            // external interrupt RB0/INT pin?
            if(INTF == 1 && INTE == 1) {
                mem.setINTCONRegValue(7, 0); // clear GIE
                if(Integer.parseInt(mem.getLastOpcode(),16) > mem.getPCL()) {
                    mem.push(mem.getPCL()+1);
                }
                else {
                    mem.push(Integer.parseInt(mem.getLastOpcode(),16));
                }
                mem.setPCL(4);
            }

            // PORTB change interrupt?
            if(RBIF == 1 && RBIE == 1) {
                mem.setINTCONRegValue(7, 0); // clear GIE
                if(Integer.parseInt(mem.getLastOpcode(),16) > mem.getPCL()) {
                    mem.push(mem.getPCL()+1);
                }
                else {
                    mem.push(Integer.parseInt(mem.getLastOpcode(),16));
                }
                mem.setPCL(4);
            }

        }
        

    }


    /**
     *
     * @param instruction (String)
     * @brief Ruft die Methoden des entprechenden Befehls auf. Gilt nur für Befehle ohne Parameter.
     */
    public void executeOperation(String instruction) {

        if(instruction.equals("clrw")) {
            clrw(); // [3]
        }
        else if(instruction.equals("nop")) {
            nop(); // [12]
        }
        else if(instruction.equals("clrwdt")) {
            clrwdt(); // [25]
        }
        else if(instruction.equals("retfie")) {
            retfie(); // [29]
        }
        else if(instruction.equals("return")) {
            reTurn(); // [31]
        }
        else if(instruction.equals("sleep")) {
            sleep(); // [32]
        }

    }
    /**
     * 
     * @param instruction (String)
     * @param value (int)
     * @brief Ruft die Methode des entsprechenden Befehls auf. Gilt nur für Befehle mit einem Parameter.
     */
    public void executeOperation(String instruction, int value) {

        if(instruction.equals("clrf")) {
            clrf(value); // [2]
        }
        else if(instruction.equals("movlw")) {
            movlw(value); // [11]
        }
        else if(instruction.equals("addlw")) {
            addlw(value); // [22]
        }
        else if(instruction.equals("andlw")) {
            andlw(value); // [23]
        }
        else if(instruction.equals("call")) {
            call(value); // [24]
        }
        else if(instruction.equals("goto")) {
            goTo(value); // [26]
        }
        else if(instruction.equals("iorlw")) {
            iorlw(value); // [27]
        }
        else if(instruction.equals("movwf")) {
            movwf(value); // [28] // destination is always 1
        }
        else if(instruction.equals("retlw")) {
            retlw(value); // [29]
        }
        else if(instruction.equals("sublw")) {
            sublw(value); // [33]
        }
        else if(instruction.equals("xorlw")) {
            xorlw(value); // [34]
        }
        else {
            System.out.println("BEFEHL ("+instruction+") HIER NICHT GEFUNDEN!!");
        }
                
        
    }

    /**
     * 
     * @param instruction (String)
     * @param address (int)
     * @param dest (boolean)
     * @brief Ruft die Methode des entsprechenden Befehls auf. Gilt nur für Befehle mit zwei Parametern. (int, bool)
     */
    public boolean executeOperation(String instruction, int address, boolean dest) {

        if(instruction.equals("addwf")) {
            addwf(address, dest); // [0]
            return true;
        }
        else if(instruction.equals("andwf")) {
            andwf(address, dest); // [1]
            return true;
        }
        else if(instruction.equals("comf")) {
            comf(address, dest);// [4]
            return true;
        }
        else if(instruction.equals("decf")) {
            decf(address, dest); // [5]
            return true;
        }
        else if(instruction.equals("decfsz")) {
            decfsz(address, dest); // [6]
            return true;
        }
        else if(instruction.equals("incf")) {
            incf(address, dest); // [7]
            return true;
        }
        else if(instruction.equals("incfsz")) {
            incfsz(address, dest); // [8]
            return true;
        }
        else if(instruction.equals("iorwf")) {
            iorwf(address, dest); // [9]
            return true;
        }
        else if(instruction.equals("movf")) {
            movf(address, dest); // [10]
            return true;
        }
        else if(instruction.equals("movlw")) {
            movlw(address); // [11]
            return true;
        }
        else if(instruction.equals("rlf")) {
            rlf(address, dest); // [13]
            return true;
        }
        else if(instruction.equals("rrf")) {
            rrf(address, dest); // [14]
            return true;
        }
        else if(instruction.equals("subwf")) {
            subwf(address, dest); // [15]
            return true;
        }
        else if(instruction.equals("swapf")) {
            swapf(address, dest); // [16]
            return true;
        }
        else if(instruction.equals("xorwf")) {
            xorwf(address, dest); // [17]
            return true;
        }        
        else {
            //System.out.println("BEFEHL ("+instruction+") HIER NICHT GEFUNDEN!!");
            return false;
        }

    }


    /**
     * 
     * @param instruction (String)
     * @param address (int)
     * @param bits (int)
     * @brief Ruft die Methode des entsprechenden Befehls auf. Gilt nur für Befehle mit zwei Parametern (int, int).
     */
    public void executeOperation(String instruction, int address, int bits) {

        if(instruction.equals("bcf")) {
            bcf(address, bits); // [18]
        }
        else if(instruction.equals("bsf")) {
            bsf(address, bits); // [19]
        }
        else if(instruction.equals("btfsc")) {
            btfsc(address, bits); // [20]
        }
        else if(instruction.equals("btfss")) {
            btfss(address, bits); // [21]
        }
        else {
            System.out.println("BEFEHL ("+instruction+") HIER NICHT GEFUNDEN!!");
        }

    }

    /**
     * 
     * @param a (int)
     * @param b (int)
     * @brief Prüft anhand der übergebenen Werte, ob die Summe der 4 niederwertigsten Bits größer als 15 sind.
     * Falls ja, wird das Digit Carry (DC) im Speicher gesetzt (= 1). Ansonsten wird es gelöscht (= 0)
     */
    private void setDigitCarry(int a, int b) {

        String stringA = calc.fillToBinaryString(8, a);
        String stringB = calc.fillToBinaryString(8, b);
       
        stringA = stringA.substring(4); // get last 4 digits of a
        stringB = stringB.substring(4); // get last 4 digits of b

        int partA = Integer.parseInt(stringA,2); // get string as int
        int partB = Integer.parseInt(stringB,2); // get string as int

        if((partA + partB) > 15) { // bigger than 1111? -> there's a carry-out
            mem.setStatusRegValue(1, 1); // set digit carry
        }
        else mem.setStatusRegValue(1, 0); // clear digit carry

    }

    

    // [0]
    // Status affected: C, DC, Z
    private void addwf(int f, boolean d) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int w = mem.getW();
        int index = mem.getSramIndex(f);

        if(index == -1) { // no sram
            result = w + mem.getSFRValue(f);
        }
        else {
            result = w + mem.getSramValue(index);
        }

        // Carry bit
        if(result > 255) {
            mem.setStatusRegValue(0, 1); // set Carry bit
            result = result - 255;
        }
        else mem.setStatusRegValue(0, 0); // clear Carry bit

        // Digit carry
        if(index == -1) { // no sram
            setDigitCarry(w, mem.getSFRValue(f));
        }
        else {
            setDigitCarry(w, mem.getSramValue(index));
        }

        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, result);
            }
            else mem.setSramValue(index, result);
        }
        else { // save to w
            //mem.setW(result);
            int index_new = mem.getSramIndex(result-w);
            System.out.println();
            int sramVal = mem.getSramValue(index_new);
            System.out.println();

            result = w+sramVal;

            mem.setW(result);

            System.out.println();

        }


        // Zero Bit
        if(result == 0) {
            mem.setStatusRegValue(2, 1); // set zero bit
        }
        else mem.setStatusRegValue(2, 0); // clear zero bit

    }

    // [1]
    // Status Affected: Z
    private void andwf(int f, boolean d) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int w = mem.getW();
        int index = mem.getSramIndex(f);

        if(index == -1) { // no sram
            result = w & mem.getSFRValue(f);
        }
        else {
            result = w & mem.getSramValue(index);
        }

        // Zero Bit
        if(result == 0) {
            mem.setStatusRegValue(2, 1); // set zero bit
        }
        else mem.setStatusRegValue(2, 0); // clear zero bit

        
        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, result);
            }
            else mem.setSramValue(index, result);
        }
        else { // save to w
            mem.setW(result);
        }

    }

    // [2]
    // Status Affected: Z
    private void clrf(int f) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            mem.setSFRValue(f, 0);
        }
        else { // sram
            mem.setSramValue(index, 0);
        }
        
        mem.setStatusRegValue(2, 1); // set zero bit

    }

    // [3]
    // Status Affected: Z
    private void clrw() {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        mem.setW(0);
        mem.setStatusRegValue(2, 1); // set zero bit

    }

    // [4]
    // Status Affected: Z
    private void comf(int f, boolean d) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        String result_str = "";

        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            result = mem.getSFRValue(f);
            result_str = calc.fillToBinaryString(8, result);
            result_str = result_str.replace("1", "x"); // all 1 are "converted" to x
            result_str = result_str.replace("0", "1"); // all 0 are replaced with 1 (negate 0)
            result_str = result_str.replace("x", "0"); // now we can replace the old 1 (x) with 0 (negate 1)
        }
        else {
            result = mem.getSramValue(index);
            result_str = calc.fillToBinaryString(8, result);
            result_str = result_str.replace("1", "x"); // all 1 are "converted" to x
            result_str = result_str.replace("0", "1"); // all 0 are replaced with 1 (negate 0)
            result_str = result_str.replace("x", "0"); // now we can replace the old 1 (x) with 0 (negate 1)            
        }

        result = Integer.parseInt(result_str, 2);

        // Zero Bit
        if(result == 0) {
            mem.setStatusRegValue(2, 1); // set zero bit
        }
        else mem.setStatusRegValue(2, 0); // clear zero bit



        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, result);
            }
            else {
                mem.setSramValue(index, result);
            }
        }
        else { // save to w
            mem.setW(result);
        }

    }

    // [5]
    // Status Affected: Z
    private void decf(int f, boolean d) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int index = mem.getSramIndex(f);
        if(index == -1) {
            result = mem.getSFRValue(f) - 1;
        }
        else {
            result = mem.getSramValue(index) - 1;
        }

        // Zero Bit
        if(result == 0) {
            mem.setStatusRegValue(2, 1); // set zero bit
        }
        else mem.setStatusRegValue(2, 0); // clear zero bit
        

        if(d) { // save to f
            if(index == -1) { // no sram 
                mem.setSFRValue(f, result);
            }
            else {
                mem.setSramValue(index, result);
            }
        }
        else { // save to w
            mem.setW(result);
        }

    }

    // [6]
    // Status Affected: None
    private void decfsz(int f, boolean d) {

        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            result = mem.getSFRValue(f) - 1;
        }
        else {
            result = mem.getSramValue(index) - 1;
        }

        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, result);
            }
            else {
                mem.setSramValue(index, result);
            }
        }
        else { // save to w
            mem.setW(result);
        }

        if(result == 0) {            
            mem.setPCL(); // pcl++
            mem.setPCL(); // pcl++
            nopForCycle();
        } // makes it a two cycles instruction
        else mem.setPCL(); // pcl++

    }

    // [7]
    // Status Affected: Z
    private void incf(int f, boolean d) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            result = mem.getSFRValue(f) + 1;
        }
        else {
            result = mem.getSramValue(index) + 1;
        }

        // Zero Bit
        if(result == 0) {
            mem.setStatusRegValue(2, 1); // set zero bit
        }
        else mem.setStatusRegValue(2, 0); // clear zero bit
        

        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, result);
            }
            else {
                mem.setSramValue(index, result);
            }
        }
        else { // save to w
            mem.setW(result);
        }

    }

    // [8]
    // Status Affected: None
    private void incfsz(int f, boolean d) {

        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++
        
        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            result = mem.getSFRValue(f) + 1;
        }
        else {
            result = mem.getSramValue(index) + 1;
        }

        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, result);
            }
            else {
                mem.setSramValue(index, result);
            }
        }
        else { // save to w
            mem.setW(result);
        }

        if(result == 0) nopForCycle(); // makes it a two cycles instruction
        else mem.setPCL(); // pcl++

    }

    // [9]
    // Status Affected: Z
    private void iorwf(int f, boolean d) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int w = mem.getW();
        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            result = w | mem.getSFRValue(f);
        }
        else {
            result = w | mem.getSramValue(index);
        }

        // Zero Bit
        if(result == 0) {
            mem.setStatusRegValue(2, 1); // set zero bit
        }
        else mem.setStatusRegValue(2, 0); // clear zero bit

        
        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, result);
            }
            else {
                mem.setSramValue(index, result);
            }
        }
        else { // save to w
            mem.setW(result);
        }

    }

    // [10]
    // Status Affected: Z
    private void movf(int f, boolean d) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            result = mem.getSFRValue(f);
        }
        else {
            result = mem.getSramValue(index);
        }

        // Zero Bit
        if(result == 0) {
            mem.setStatusRegValue(2, 1); // set zero bit
        }
        else mem.setStatusRegValue(2, 0); // clear zero bit
        

        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, result);
            }
            else {
                mem.setSramValue(index, result);
            }

        }
        else { // save to w
            mem.setW(result);
        }

    }

    // [11]
    // Status Affected: None
        private void movwf(int f) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            mem.setSFRValue(f, mem.getW());
        }
        else {
            mem.setSramValue(index, mem.getW());
        }
        
    }

    // [12]
    // Status Affected: None
    private void nop() {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        // TODO
        System.out.println("nop is executed");

    }

    // Status Affected: None
    private void nopForCycle() {

        // make some operations a two cycle instruction
        mem.incRealTimeDuration(); // realTime++

    }

    // [13]
    // Status Affected: C
    private void rlf(int f, boolean d) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int value = 0;

        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            value = mem.getSFRValue(f);
        }
        else {
            value = mem.getSramValue(index);
        }
        
        String stringResult = Integer.toBinaryString(value);
        int diff = 8 - stringResult.length(); // each GPRs are 8 bits wide

        // fill prefix with 0
        for(int i=0; i<diff; i++) {
            stringResult = "0" + stringResult;
        }

        String carry = stringResult.substring(0, 1); // first bit for carry flag
        stringResult = stringResult.substring(1) + Integer.toString(mem.getStatusRegValue(0)); // get rest of String and add (old) carry behind

        mem.setStatusRegValue(0, Integer.parseInt(carry,2)); // set carry bit

        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, Integer.parseInt(stringResult,2));
            }
            else {
                mem.setSramValue(index, Integer.parseInt(stringResult,2));
            }
        }
        else { // save to w
            mem.setW(Integer.parseInt(stringResult,2));
        }

    }

    // [14]
    // Status Affected: C
    private void rrf(int f, boolean d) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int value = 0;

        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            value = mem.getSFRValue(f);
        }
        else {
            value = mem.getSramValue(index);
        }

        String stringResult = Integer.toBinaryString(value);
        int diff = 8 - stringResult.length(); // each GPRs are 8 bits wide

        // fill prefix with 0
        for(int i=0; i<diff; i++) {
            stringResult = "0" + stringResult;
        }

        String carry = stringResult.substring(stringResult.length()-1); // last bit for carry flag
        stringResult = Integer.toString(mem.getStatusRegValue(0)) + stringResult.substring(0, stringResult.length()-1); // add (old) carry in front and get rest of String

        mem.setStatusRegValue(0, Integer.parseInt(carry,2)); // set carry bit

        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, Integer.parseInt(stringResult,2));
            }
            else {
                mem.setSramValue(index, Integer.parseInt(stringResult,2));
            }
        }
        else { // save to w
            mem.setW(Integer.parseInt(stringResult,2));
        }

    }

    // [15]
    // Status Affected: C, DC, Z
    private void subwf(int f, boolean d) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int w = mem.getW();
        int index = mem.getSramIndex(f);
        if(index == -1) {
            result = mem.getSFRValue(f) - w; // substract w from f
        }
        else {
            result = mem.getSramValue(index) - w; // substract w from f
        }

        // Carry bit
        if(result > 255) {
            mem.setStatusRegValue(0, 1); // set Carry bit
            result = result - 255;            
        }
        else if(result < 0) {
            mem.setStatusRegValue(0, 0); // clear Carry bit
            result = result * -1;
        }
        else mem.setStatusRegValue(0, 0); // clear Carry bit

        // Digit carry
        if(index == -1) { // no sram
            setDigitCarry(w, mem.getSFRValue(f));
        }
        else {
            setDigitCarry(w, mem.getSramValue(index));
        }
        

        // Zero Bit
        if(result == 0) {
            mem.setStatusRegValue(2, 1); // set zero bit
        }
        else mem.setStatusRegValue(2, 0); // clear zero bit
        

        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, result);
            }
            else {
                mem.setSramValue(index, result);
            }
        }
        else { // save to w
            mem.setW(result);
        }

    }

    // [16]
    // Status Affected: None
    private void swapf(int f, boolean d) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int value = 0;

        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            value = mem.getSFRValue(f);
        }
        else {
            value = mem.getSramValue(index);
        }
        
        String stringResult = Integer.toBinaryString(value);
        int diff = 8 - stringResult.length(); // each GPRs are 8 bits wide

        // fill prefix with 0
        for(int i=0; i<diff; i++) {
            stringResult = "0" + stringResult;
        }

        String upper = stringResult.substring(0,4); // get upper bits (4)
        String lower = stringResult.substring(4); // get lower bits (4)
        stringResult =  lower+upper; // swap bits

        result = Integer.parseInt(stringResult,2);

        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, result);
            }
            else {
                mem.setSramValue(index, result);
            }
        }
        else { // save to w
            mem.setW(result);
        }

    }

    // [17]
    // Status Affected: Z
    private void xorwf(int f, boolean d) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int w = mem.getW();
        int index = mem.getSramIndex(f);
        if(index == -1) {
            result = w ^ mem.getSFRValue(f); // xor
        }
        else {
            result = w ^ mem.getSramValue(index); // xor
        }

        // Zero Bit
        if(result == 0) mem.setStatusRegValue(2, 1); // set zero bit
        else mem.setStatusRegValue(2, 0); // clear zero bit

        if(d) { // save to f
            if(index == -1) { // no sram
                mem.setSFRValue(f, result);
            }
            else {
                mem.setSramValue(index, result);
            }
        }
        else { // save to w
            mem.setW(result);
        }

    }


    // [18]
    // Status Affected: None
    private void bcf(int f, int b) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int value = 0;

        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            value = mem.getSFRValue(f);
        }
        else { // adress is in sram
            value = mem.getSramValue(index);
        }

        switch(b) {
            case 0: result = value & 0xFE; break; // 1111 1110 (dual)
            case 1: result = value & 0xFD; break; // 1111 1101 (dual)
            case 2: result = value & 0xFB; break; // 1111 1011 (dual)
            case 3: result = value & 0xF7; break; // 1111 0111 (dual)

            case 4: result = value & 0xEF; break; // 1110 1111 (dual)
            case 5: result = value & 0xDF; break; // 1101 1111 (dual)
            case 6: result = value & 0xBF; break; // 1011 1111 (dual)
            case 7: result = value & 0x7F; break; // 0111 1111 (dual)
        }


        if(index == -1) { // no sram
            mem.setSFRValue(f, result);
        }
        else { // address is in sram
            mem.setSramValue(index, result);
        }

    }

    // [19]
    // Status Affected: None
    private void bsf(int f, int b) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int value = 0;
        
        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            value = mem.getSFRValue(f);
        }
        else { // address is in sram
            
            value = mem.getSramValue(index);
        }

        String stringResult = Integer.toBinaryString(value);
        int diff = 8 - stringResult.length(); // each GPRs are 8 bits wide

        // fill prefix with 0
        for(int i=0; i<diff; i++) {
            stringResult = "0" + stringResult;
        }

        // negate on pos b
        // be careful on start...
        if(b == 0) {
            stringResult = stringResult.substring(0,7) + "1";
        }
        // ... and on end
        else if(b==7) {
            stringResult = "1" + stringResult.substring(1);
        }
        else {

            int pos = 0;
            switch(b) { // string pos 1 = int pos 6, string pos 2 = int pos 5, ...
                case 1: pos = 6; break;
                case 2: pos = 5; break;
                case 3: pos = 4; break;
                case 4: pos = 3; break;
                case 5: pos = 2; break;
                case 6: pos = 1; break;
                    default: System.out.println("[PIC:bsf]: Fehler!");
            }

            // if on pos b the value is 1 there is nothing to do
            if(stringResult.substring(pos, pos+1).equals("0")) {
                stringResult =  stringResult.substring(0,pos) + "1" + stringResult.substring(pos+1);
            }
        }

        if(index == -1) { // no sram
            mem.setSFRValue(f, Integer.parseInt(stringResult,2));
        }
        else { // address is in sram
            mem.setSramValue(index, Integer.parseInt(stringResult,2));
        }

    }

    // [20]
    // Status Affected: None
    private void btfsc(int f, int b) {

        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        int value = 0;

        int index = mem.getSramIndex(f);
        if(index == -1) { // no sram
            value = mem.getSFRValue(f);
        }
        else { // address is in sram
            value = mem.getSramValue(index);
        }

        // fill prefix with 0
        String stringResult = calc.fillToBinaryString(8, value);

        // negate on pos b
        // be careful on start...
        if(b == 0) {
            if(stringResult.substring(7).equals("1")) {
                mem.setPCL(); // pcl++
            }
            else {                 
                mem.setPCL(); // pcl++
                mem.setPCL(); // pcl++
                nopForCycle(); // makes it a two cycles instruction
            }
        }
        // ... and on end
        else if(b==7) {
            if(stringResult.substring(0,1).equals("1")) {
                mem.setPCL(); // pcl++
            }
            else {
                mem.setPCL(); // pcl++
                mem.setPCL(); // pcl++
                nopForCycle(); // makes it a two cycles instruction
            }
        }
        else {

            int pos = 0;
            switch(b) { // string pos 1 = int pos 6, string pos 2 = int pos 5, ...
                case 1: pos = 6; break;
                case 2: pos = 5; break;
                case 3: pos = 4; break;
                case 4: pos = 3; break;
                case 5: pos = 2; break;
                case 6: pos = 1; break;
                    default: System.out.println("[PIC:bsf]: Fehler!");
            }

            if(stringResult.substring(pos,pos+1).equals("1")) {
                mem.setPCL(); // pcl++
            }
            else {                
                mem.setPCL(); // pcl++
                mem.setPCL(); // pcl++
                nopForCycle(); // makes it a two cycles instruction
            }
            
        }

    }

    // [21]
    // Status Affected: None
    private void btfss(int f, int b) {

        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++
        
        int value = 0;

        int index = mem.getSramIndex(f);
        if (index == -1){ // no sram
            value = mem.getSFRValue(f);
        }
        else {
            value = mem.getSramValue(index);
        }

        // fill prefix with 0
        String stringResult = calc.fillToBinaryString(8, value);

        // negate on pos b
        // be careful on start...
        if(b == 0) {
            if(stringResult.substring(7).equals("0")) {
                mem.setPCL(); // pcl++
            }
            else {                
                mem.setPCL(); // pcl++
                mem.setPCL(); // pcl++
                nopForCycle(); // makes it a two cycles instruction
            }
        }
        // ... and on end
        else if(b==7) {
            if(stringResult.substring(0,1).equals("0")) {
                mem.setPCL(); // pcl++
            }
            else {                 
                mem.setPCL(); // pcl++
                mem.setPCL(); // pcl++
                nopForCycle(); // makes it a two cycles instruction
            }
        }
        else {

            int pos = 0;
            switch(b) { // string pos 1 = int pos 6, string pos 2 = int pos 5, ...
                case 1: pos = 6; break;
                case 2: pos = 5; break;
                case 3: pos = 4; break;
                case 4: pos = 3; break;
                case 5: pos = 2; break;
                case 6: pos = 1; break;
                    default: System.out.println("[PIC:bsf]: Fehler!");
            }

            if(stringResult.substring(pos,pos+1).equals("0")) {
                mem.setPCL(); // pcl++
            }
            else {                 
                mem.setPCL(); // pcl++
                mem.setPCL(); // pcl++
                nopForCycle(); // makes it a two cycles instruction
            }

        }

    } 


    // [22]
    // Status Affected: C, DC, Z
    private void addlw(int k) {

        mem.setPCL(); // pcl++

        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        result = k + mem.getW();

        // Carry bit
        if(result > 255) {
            mem.setStatusRegValue(0, 1); // set Carry bit
            result = result - 255;
        }
        else mem.setStatusRegValue(0, 0); // clear Carry bit

        // Digit carry
        setDigitCarry(mem.getW(), k);

        // Zero Bit
        if(result == 0) {
            mem.setStatusRegValue(2, 1); // set zero bit
        }
        else mem.setStatusRegValue(2, 0); // clear zero bit
        

        mem.setW(result);
        
    }

    // [23]
    // Status Affected: Z
    private void andlw(int k) {

        mem.setPCL(); // pcl++

        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        result = mem.getW() & k;

        // Zero Bit

        if(result == 0) {
            mem.setStatusRegValue(2, 1); // set zero bit
        }
        else mem.setStatusRegValue(2, 0); // clear zero bit

        mem.setW(result);

    }

    // [24]
    // Status Affected: None
    private void call(int k) {

        // call is a two cyle instruction!!
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        mem.setPCL(k); // goto subroutine

        nopForCycle(); // makes it a two cycles instruction

        

    }

    // [25]
    // Status Affected: ^TO, ^PD
    private void clrwdt() {

        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        // TODO

    }

    // [26]
    // Status Affected: None
    private void goTo(int memAddr) {

        // goto is a two cycle instruction!!
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        nopForCycle(); // makes it a two cycles instruction

        mem.setPCL(memAddr); // jump to new line in which memAddr is found

    }

    // [27]
    // Status Affected: Z
    private void iorlw(int k) {

        mem.setPCL(); // pcl++

        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        result = mem.getW() | k;

        // Zero Bit
        if(result == 0) {
            mem.setStatusRegValue(2, 1); // set zero bit
        }
        else mem.setStatusRegValue(2, 0); // clear zero bit

        mem.setW(result);

    }
    
    // [28]
    // Status Affected: None
    private void movlw(int k) {
        
        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        mem.setW(k); // literal

    }

    // [29]
    // Status Affected: None
    private void retfie() {

        // retfie is a two cyle instruction!!
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        nopForCycle(); // makes it a two cycles instruction

        mem.setINTCONRegValue(7, 1); // re-enable interrupts
        mem.setPCL(mem.pop()); // go back in regular program

    }

    // [30]
    // Status Affected: None
    private void retlw(int k) {

        mem.setPCL(); // pcl++
        
        // retlw is a two cyle instruction!!
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        nopForCycle(); // makes it a two cycles instruction

        mem.setW(k);

        mem.setPCL(mem.pop());

    }

    // [31]
    // Status Affected: None
    private void reTurn() {

        // return is a two cyle instruction!!
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        mem.setPCL(mem.pop()); 

        nopForCycle(); // makes it a two cycles instruction
        // TODO

    }

    // [32]
    // Status Affected: ^TO, ^PD
    private void sleep() {

        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        mem.setStatusRegValue(3, 0); // clear ^PD
        mem.setStatusRegValue(4, 1); // set ^PD

    }

    // [33]
    // Status Affected: C, DC, Z
    private void sublw(int k) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        result = k - mem.getW(); // substract k from w

        // Carry bit
        if(result > 255) {
            mem.setStatusRegValue(0, 1); // set Carry bit
            result = result - 255;
        }
        else if(result < 0) {
            mem.setStatusRegValue(0, 0); // clear Carry bit
            result = result * -1;
        }
        else mem.setStatusRegValue(0, 0); // clear Carry bit

        // Digit carry
        setDigitCarry(mem.getW(), k);

        // Zero Bit
        if(result == 0) {
            mem.setStatusRegValue(2, 1); // set zero bit
        }
        else mem.setStatusRegValue(2, 0); // clear zero bit

        mem.setW(result);

    }

    // [34]
    // Status Affected: Z
    private void xorlw(int k) {

        mem.setPCL(); // pcl++
        mem.incRealTimeDuration(); // realTime++
        mem.incInstCounter(); // instCounter++

        result = mem.getW() ^ k; // xor

        // Zero Bit
        if(result == 0) mem.setStatusRegValue(2, 1); // set zero bit
        else mem.setStatusRegValue(2, 0); // clear zero bit

        mem.setW(result);

    }

}
